/*
 * Copyright (C) 2005 Sascha Hauer, Pengutronix
 * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the version 2 of the GNU General Public License
 * as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/can/dev.h>
#include <linux/can/platform/sja1000.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>

#include "mx6.h"
#include "sja1000.h"

#define DRV_NAME "sja1000_platform"
#define SP_CAN_CLOCK  (16000000 / 2)

void mx6q_setup_weimcs(void);
static void imx6_baseboard_init(void);
static void imx6_baseboard_exit(void);

MODULE_AUTHOR("Westlor <westlor@foxmail.com>");
MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus");
MODULE_LICENSE("GPL v2");

static u8 sp_read_reg8(const struct sja1000_priv *priv, int reg)
{
	u16 data;
	data = ioread16(priv->reg_base + reg*2);
	return data>>1;
}

static void sp_write_reg8(const struct sja1000_priv *priv, int reg, u8 val)
{
	u16 data=val;
	data = data*2;
	iowrite16(data + priv->wdataOffset, priv->reg_base + reg*2);
}

static u8 sp_read_reg16(const struct sja1000_priv *priv, int reg)
{
	return ioread8(priv->reg_base + reg * 2);
}

static void sp_write_reg16(const struct sja1000_priv *priv, int reg, u8 val)
{
	iowrite8(val, priv->reg_base + reg * 2);
}

static u8 sp_read_reg32(const struct sja1000_priv *priv, int reg)
{
	return ioread8(priv->reg_base + reg * 4);
}

static void sp_write_reg32(const struct sja1000_priv *priv, int reg, u8 val)
{
	iowrite8(val, priv->reg_base + reg * 4);
}

static void sp_populate(struct sja1000_priv *priv,
			struct sja1000_platform_data *pdata,
			unsigned long resource_mem_flags)
{
	/* The CAN clock frequency is half the oscillator clock frequency */
	priv->can.clock.freq = pdata->osc_freq / 2;
	priv->ocr = pdata->ocr;
	priv->cdr = pdata->cdr;

	switch (resource_mem_flags & IORESOURCE_MEM_TYPE_MASK) {
	case IORESOURCE_MEM_32BIT:
		priv->read_reg = sp_read_reg32;
		priv->write_reg = sp_write_reg32;
		break;
	case IORESOURCE_MEM_16BIT:
		priv->read_reg = sp_read_reg16;
		priv->write_reg = sp_write_reg16;
		break;
	case IORESOURCE_MEM_8BIT:
	default:
		priv->read_reg = sp_read_reg8;
		priv->write_reg = sp_write_reg8;
		break;
	}
}

static void sp_populate_of(struct sja1000_priv *priv, struct device_node *of)
{
	int err;
	u32 prop;

	err = of_property_read_u32(of, "reg-io-width", &prop);
	if (err)
		prop = 1; /* 8 bit is default */

	switch (prop) {
	case 4:
		priv->read_reg = sp_read_reg32;
		priv->write_reg = sp_write_reg32;
		break;
	case 2:
		priv->read_reg = sp_read_reg16;
		priv->write_reg = sp_write_reg16;
		break;
	case 1:	/* fallthrough */
	default:
		priv->read_reg = sp_read_reg8;
		priv->write_reg = sp_write_reg8;
	}

	err = of_property_read_u32(of, "nxp,external-clock-frequency", &prop);
	if (!err)
		priv->can.clock.freq = prop / 2;
	else
		priv->can.clock.freq = SP_CAN_CLOCK; /* default */

	err = of_property_read_u32(of, "nxp,tx-output-mode", &prop);
	if (!err)
		priv->ocr |= prop & OCR_MODE_MASK;
	else
		priv->ocr |= OCR_MODE_NORMAL; /* default */

	err = of_property_read_u32(of, "nxp,tx-output-config", &prop);
	if (!err)
		priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK;
	else
		priv->ocr |= OCR_TX0_PULLDOWN; /* default */

	err = of_property_read_u32(of, "nxp,clock-out-frequency", &prop);
	if (!err && prop) {
		u32 divider = priv->can.clock.freq * 2 / prop;

		if (divider > 1)
			priv->cdr |= divider / 2 - 1;
		else
			priv->cdr |= CDR_CLKOUT_MASK;
	} else {
		priv->cdr |= CDR_CLK_OFF; /* default */
	}

	if (!of_property_read_bool(of, "nxp,no-comparator-bypass"))
		priv->cdr |= CDR_CBP; /* default */
}

static int sp_probe(struct platform_device *pdev)
{
	int err, irq = 0;
	int irq_pin = -1;
	int reset_pin = -1;
	int led_pin = -1;
	void __iomem *addr;
	struct net_device *dev;
	struct sja1000_priv *priv;
	struct resource *res_mem, *res_irq = NULL;
	struct sja1000_platform_data *pdata;
	struct device_node *of = pdev->dev.of_node;

	pdata = dev_get_platdata(&pdev->dev);
	if (!pdata && !of) {
		dev_err(&pdev->dev, "No platform data provided!\n");
		return -ENODEV;
	}

	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res_mem)
		return -ENODEV;

	if (!devm_request_mem_region(&pdev->dev, res_mem->start,
				     resource_size(res_mem), DRV_NAME))
		return -EBUSY;

	addr = devm_ioremap_nocache(&pdev->dev, res_mem->start,
				    resource_size(res_mem));
	if (!addr)
		return -ENOMEM;

	if (of){
		reset_pin 	= of_get_named_gpio(of, "rst-gpios", 0);
		irq_pin 	= of_get_named_gpio(of, "int-gpios", 0);
		led_pin 	= of_get_named_gpio(of, "led-gpios", 0);
		irq = irq_of_parse_and_map(of, 0);

		if(gpio_is_valid(reset_pin)) {
			err = devm_gpio_request_one(&pdev->dev, reset_pin,
					GPIOF_OUT_INIT_HIGH, "sja1000 reset");
			if (err) {
				dev_err(&pdev->dev,
					"Failed to request GPIO %d as reset pin, error %d\n",
					reset_pin, err);
			}
		}
		if(gpio_is_valid(irq_pin)){
			err = devm_gpio_request_one(&pdev->dev, irq_pin,
					GPIOF_IN, "sja1000 int");
			if (err) {
				dev_err(&pdev->dev,
					"Failed to request GPIO %d as irq pin, error %d\n",
					irq_pin, err);
			}
		}
		if(gpio_is_valid(led_pin)) {
			err = devm_gpio_request_one(&pdev->dev, led_pin,
					GPIOF_OUT_INIT_LOW, "sja1000 led");
			if (err) {
				dev_err(&pdev->dev,
					"Failed to request GPIO %d as led pin, error %d\n",
					irq_pin, err);
			}
		}
	}else{
		res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	}

	if (!irq && !res_irq)
		return -ENODEV;

	dev = alloc_sja1000dev(0);
	if (!dev)
		return -ENOMEM;
	priv = netdev_priv(dev);

	if (res_irq) {
		irq = res_irq->start;
		priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
		if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
			priv->irq_flags |= IRQF_SHARED;
	} else {
		//priv->irq_flags = IRQF_SHARED;
		priv->irq_flags |= IRQF_TRIGGER_LOW;
		//priv->irq_flags |= IRQF_TRIGGER_FALLING;
	}

	dev->irq = irq;
	priv->reg_base 	= addr;
	priv->irq_pin 	= irq_pin;
	priv->reset_pin = reset_pin;
	priv->led_pin 	= led_pin;
	priv->led_status = 0;
	priv->wdataOffset = res_mem->start - 0x08000000;		/* add write data offset */

	printk(KERN_INFO "irq compare: res_irq--%d, gpio_to_irq--%d\n", irq, gpio_to_irq(irq_pin));
	printk(KERN_INFO "iomap: %x-%x\n", res_mem->start, res_mem->start+resource_size(res_mem));
	printk(KERN_INFO "write data offset: %x\n", priv->wdataOffset);
	printk(KERN_INFO "IRQF: %lx\n", priv->irq_flags);

	if (of)
		sp_populate_of(priv, of);
	else
		sp_populate(priv, pdata, res_mem->flags);

	platform_set_drvdata(pdev, dev);
	SET_NETDEV_DEV(dev, &pdev->dev);

	err = register_sja1000dev(dev);
	if (err) {
		dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
			DRV_NAME, err);
		goto exit_free;
	}

	dev_info(&pdev->dev, "%s device registered (reg_base=%p, irq=%d)\n",
		 DRV_NAME, priv->reg_base, dev->irq);
	return 0;

 exit_free:
	free_sja1000dev(dev);
	return err;
}

static int sp_remove(struct platform_device *pdev)
{
	struct net_device *dev = platform_get_drvdata(pdev);

	unregister_sja1000dev(dev);
	free_sja1000dev(dev);

	return 0;
}

static struct of_device_id sp_of_table[] = {
	{.compatible = "weim,sja1000"},
	{},
};
MODULE_DEVICE_TABLE(of, sp_of_table);

static struct platform_driver sp_driver = {
	.probe = sp_probe,
	.remove = sp_remove,
	.driver = {
		.name = DRV_NAME,
		.owner = THIS_MODULE,
		.of_match_table = sp_of_table,
	},
};

static int __init sp_init(void)
{
	imx6_baseboard_init();
	return platform_driver_register(&sp_driver);
}

static void __exit sp_exit(void)
{
	imx6_baseboard_exit();
	platform_driver_unregister(&sp_driver);
}

/*
 * system init for baseboard usage. Will be called by pcm038 init.
 *
 * Add platform devices present on this baseboard and init
 * them from CPU side as far as required to use them later on
 */
static void imx6_baseboard_init(void)
{
	// setup weimcs
	mx6q_setup_weimcs();
}

static void imx6_baseboard_exit(void){

	printk(KERN_INFO "%s CAN sja100_platform driver exit.\n", DRV_NAME);
}

/* These registers settings are just valid for Numonyx M29W256GL7AN6E. */
void mx6q_setup_weimcs(void)
{
	unsigned int reg;

	void __iomem *eim_reg = ioremap(WEIM_BASE_ADDR, 0x20);
	void __iomem *ccm_reg = ioremap(CCM_BASE_ADDR, 0x80);

	if(!eim_reg){
		printk("error iomem eim_reg\n");
	}
	if(!ccm_reg){
		printk("error iomem ccm_reg\n");
	}

	// divicer for aclk_eim_slow
	reg = readl(ccm_reg + 0x1C);
	reg &= ~(0x60000000);
	reg |= 0x00380000;
	writel(reg, ccm_reg + 0x1C);

	/* CLKCTL_CCGR6: Set emi_slow_clock to be on in all modes */
	reg = readl(ccm_reg + 0x80);
	reg |= 0x00000C00;
	writel(reg, ccm_reg + 0x80);

	/*
	 * For EIM General Configuration registers.
	 *
	 * CS0GCR1:
	 *	GBC = 7; CSREC = 6; DSZ = 4;
	 *	BCS = 0; BCD=3
	 *	BL = 0;
	 *	[22-20 CSREC: minimum EIM clock cycles width of CS, OE and WE signals]
	 *	DSZ[16:18]:
	    000 Reserved.
		001 16 bit port resides on DATA[15:0]
		010 16 bit port resides on DATA[31:16]
		011 32 bit port resides on DATA[31:0]
		100 8 bit port resides on DATA[7:0]
		101 8 bit port resides on DATA[15:8]
		110 8 bit port resides on DATA[23:16]
		111 8 bit port resides on DATA[31:24]
	 *
	 *	CREP[7] = 1;
	 *	MUM[3] = 1;	CSEN[0] = 1;
	 *
	 *	EIM Operation Mode: MUM=1, SRD = SWR = 0.
	 *		(Async write/Async page read, multiplexed)
	 *
	 * CS0GCR2:
	 *	ADH = 1
	 */
	writel(0x07f13039, eim_reg);
	writel(0x00001002, eim_reg + 0x00000004);

	/*
	 * For EIM Read Configuration registers.
	 *
	 * CS0RCR1:
	 * Bit 31 30 29 28--27 26 25 24--23 22 21 20--19  18 17 16
	 * 	   0     RWSC                0  RADVA     RAL RADVN
	 * Bit 15 14 13 12--11 10 9--8 7 6 5 4--3 2 1 0
	 *     0  OEA       0  OEN   0 RCSA     0 RCSN
	 * CS0RCR2:
	 *	APR = 0 (Async Page Read); 		[15]
	 *	PAT = 7 (9 EIM clock sycles)	[12:14]
	 *	RBEA = 7 (Read BE Assertion)	[4:6]
	 *	RBE = 1 (Read BE enable)		[3]
	 *	RBEN = 7 (Read BE Negation)		[0:2]
	 */
	writel(0x18683372, eim_reg + 0x00000008);
	writel(0x00000068, eim_reg + 0x0000000C);

	/*
	 * For EIM Write Configuration registers.
	 *
	 * CS0WCR1:
	 *	Bit 31  30   29 28 27 26 25 24 23 22 21 20--19 18 17 16--15
	 *	    WAL WBED WWSC              WADVA    WADVN     WBEA
	 *	    1   1	 01 1000		   011  	0--00	  11--1
	 *	Bit 14 13 12 11 10 9 8-- 7 6 5 4 3 2 1 0
	 *		WBEN     WEA     WEN   WCSA  WCSN
	 *	    111	 	 111     1--11   100   110
	 * CS0WCR2:
	 *	WBCDD = 0
	 */
	writel(0xd863ffe6, eim_reg + 0x00000010);
	writel(0x00000000, eim_reg + 0x00000014);

	printk("WEIM init end, CS0GCR1_is %x\n", readl(eim_reg));

	iounmap(eim_reg);
	iounmap(ccm_reg);
}

module_init(sp_init);
module_exit(sp_exit);
